#include "miniraces.hpp"
#include "../score/score.hpp"

#include <cmath>
#include <fstream>

MiniRaces::MiniRaces() {
	
}

MiniRaces::~MiniRaces() {
	
}

void MiniRaces::init(Engine *engine) {
	bottom.SetImage(engine->getImage("data/miniraces/pelonia-bottom.png"));
	road.SetImage(engine->getImage("data/miniraces/pelonia-road.png"));
	top.SetImage(engine->getImage("data/miniraces/pelonia-top.png"));
	
	for(std::map<int, Player>::iterator iPlayer = engine->players.begin(); iPlayer != engine->players.end(); iPlayer++) {
		sf::Sprite car;
		car.SetImage(engine->getImage("data/miniraces/car.png"));
		car.SetColor((*iPlayer).second.color);
		car.SetCenter(sf::Vector2f(car.GetImage()->GetWidth() / 2.f, car.GetImage()->GetHeight() / 2.f));
		car.SetPosition(sf::Vector2f(50.f, 290.f));
		
		cars[(*iPlayer).first].sprite = car;
		cars[(*iPlayer).first].speed = 0.f;
		cars[(*iPlayer).first].checkPoint = 0;
	}
	
	std::ifstream raceFile("data/miniraces/pelonia.txt");
	
	do {
		CheckPoint cp;
		raceFile >> cp.x;
		if(raceFile.eof()) break;
		raceFile >> cp.y >> cp.w >> cp.h;
		checkPoints.push_back(cp);
	} while(!raceFile.eof());
	
	raceFile.close();
	
	clock.Reset();
	
	remainingCars = cars.size();
	
	quit = false;
	quitTime = 3.f;
}

void MiniRaces::cleanup() {
	cars.clear();
	scoreStrings.clear();
	checkPoints.clear();
}

void MiniRaces::pause() {
	
}

void MiniRaces::resume() {
	clock.Reset();
}

void MiniRaces::processEvent(Engine *engine, PlayerEvent& event) {
	switch(event.type) {
		case PlayerEvent::Quit:
		{
			if(event.state)
				engine->changeState(Score::getSingleton());
			break;
		}
		default:
			break;
	}
}

void MiniRaces::update(Engine *engine) {
	float time = clock.GetElapsedTime();
	clock.Reset();
	
	for(std::list<ScoreString>::iterator iScoreString = scoreStrings.begin(); iScoreString != scoreStrings.end(); iScoreString++) {
		(*iScoreString).time -= time;
		if((*iScoreString).time <= 0.f) {
			iScoreString = scoreStrings.erase(iScoreString);
			if(iScoreString == scoreStrings.end()) break;
			continue;
		}
		
		(*iScoreString).string.Move(sf::Vector2f(0.f, -time * 32.f));
		(*iScoreString).string.SetColor(sf::Color(255, 255, 255, int(((*iScoreString).time) * 255)));
	}
	
	if(quit) {
		quitTime -= time;
		if(quitTime <= 0.f) engine->changeState(Score::getSingleton());
	}	
	
	for(std::map<int, Car>::iterator iCar = cars.begin(); iCar != cars.end(); iCar++) {
		int index = (*iCar).first;
		Car& car = (*iCar).second;
		
		float goalAngle = car.sprite.GetRotation();
		
		if((engine->players[index].actions[PlayerEvent::MoveLeft].state) && (!engine->players[index].actions[PlayerEvent::MoveRight].state))
			goalAngle += 360.f * time;
		else if((engine->players[index].actions[PlayerEvent::MoveRight].state) && (!engine->players[index].actions[PlayerEvent::MoveLeft].state))
			goalAngle -= 360.f * time;
		
		/*
		if((engine->players[index].actions[PlayerEvent::MoveLeft].state) && (!engine->players[index].actions[PlayerEvent::MoveRight].state)) {
			goalAngle = 270.f;
			if((engine->players[index].actions[PlayerEvent::MoveUp].state) && (!engine->players[index].actions[PlayerEvent::MoveDown].state))
				goalAngle -= 45.f;
			else if((engine->players[index].actions[PlayerEvent::MoveDown].state) && (!engine->players[index].actions[PlayerEvent::MoveUp].state))
				goalAngle += 45.f;
		} else if((engine->players[index].actions[PlayerEvent::MoveRight].state) && (!engine->players[index].actions[PlayerEvent::MoveLeft].state)) {
			goalAngle = 90.f;
			if((engine->players[index].actions[PlayerEvent::MoveUp].state) && (!engine->players[index].actions[PlayerEvent::MoveDown].state))
				goalAngle += 45.f;
			else if((engine->players[index].actions[PlayerEvent::MoveDown].state) && (!engine->players[index].actions[PlayerEvent::MoveUp].state))
				goalAngle -= 45.f;
		} else {
			if((engine->players[index].actions[PlayerEvent::MoveUp].state) && (!engine->players[index].actions[PlayerEvent::MoveDown].state))
				goalAngle = 180.f;
			else if((engine->players[index].actions[PlayerEvent::MoveDown].state) && (!engine->players[index].actions[PlayerEvent::MoveUp].state))
				goalAngle = 0.f;
		}
		*/
		if(fabs(car.sprite.GetRotation() - goalAngle) > 180.f)
			goalAngle = -360.f + goalAngle;
		
		car.sprite.SetRotation((car.sprite.GetRotation() * 2.f + goalAngle) / 3.f);
		
		if((engine->players[index].actions[PlayerEvent::Action1].state) && (!engine->players[index].actions[PlayerEvent::Action2].state)) {
			car.speed += (300.f - car.speed) * time;
			if(car.speed > 300.f) car.speed = 300.f;
		} else if((engine->players[index].actions[PlayerEvent::Action2].state) && (!engine->players[index].actions[PlayerEvent::Action1].state)) {
			car.speed -= 100.f * time;
			if(car.speed < -100.f) car.speed = -100.f;
		} else {
			if(car.speed > 0.f) {
				car.speed -= 200.f * time;
				if(car.speed < 0.f) car.speed = 0.f;
			} else if(car.speed < 0.f) {
				car.speed += 200.f * time;
				if(car.speed > 0.f) car.speed = 0.f;
			}
		}
		
		float angle = car.sprite.GetRotation();
		
		sf::Vector2f goalMove = sf::Vector2f(sin(angle * M_PI / 180.f) * car.speed * time, cos(angle * M_PI / 180.f) * car.speed * time);
		
		// centrifugal force
		if((engine->players[index].actions[PlayerEvent::MoveLeft].state) && (!engine->players[index].actions[PlayerEvent::MoveRight].state))
			goalMove -= sf::Vector2f(cos(angle * M_PI / 180.f) * 0.01f * car.speed * time, cos(angle * M_PI / 180.f) * 0.01f * car.speed * time);
		else if((engine->players[index].actions[PlayerEvent::MoveRight].state) && (!engine->players[index].actions[PlayerEvent::MoveLeft].state))
			goalMove += sf::Vector2f(sin(angle * M_PI / 180.f) * 0.01f * car.speed * time, sin(angle * M_PI / 180.f) * 0.01f * car.speed * time);
		
		float dist = sqrt(pow(goalMove.x, 2) + pow(goalMove.y, 2));
		for(int i = 1; i < dist; i++) {
			int x = int(car.sprite.GetPosition().x + i * goalMove.x / dist);
			int y = int(car.sprite.GetPosition().y + i * goalMove.y / dist);
			
			if((x >= checkPoints[car.checkPoint].x) && (y >= checkPoints[car.checkPoint].y)
			&& (x < checkPoints[car.checkPoint].x + checkPoints[car.checkPoint].w)
			&& (y < checkPoints[car.checkPoint].y + checkPoints[car.checkPoint].h)) {
				car.checkPoint++;
				if(car.checkPoint >= checkPoints.size()) {
					if(remainingCars > 0) {
						addScore(engine, index, 20 * remainingCars / cars.size());
						remainingCars--;
						car.checkPoint = 0;
						
						if(remainingCars <= 0) quit = true;
					}
				}
			}
			
			
			if(road.GetPixel(x, y).a < 128) {
				goalMove.x = goalMove.x * i / dist;
				goalMove.y = goalMove.y * i / dist;
				car.speed = 0.f;
				break;
			}
		}
		
		car.sprite.Move(goalMove);
	}
}

void MiniRaces::draw(Engine *engine) {
	engine->window.Draw(bottom);
	engine->window.Draw(road);
	
	for(std::map<int, Car>::iterator iCar = cars.begin(); iCar != cars.end(); iCar++)
		engine->window.Draw((*iCar).second.sprite);
	
	engine->window.Draw(top);
	
	for(std::list<ScoreString>::iterator iScoreString = scoreStrings.begin(); iScoreString != scoreStrings.end(); iScoreString++)
		engine->window.Draw((*iScoreString).string);
}

void MiniRaces::addScore(Engine *engine, int player, int score) {
	engine->players[player].score += score;
	if(engine->players[player].score < 0) engine->players[player].score = 0;
	
	sf::String string;
	string.SetText(val2str(score));
	if(score > 0) string.SetText("+" + string.GetText());
	string.SetFont(engine->getFont("data/fonts/BELIEVER.TTF"));
	string.SetSize(24.f);
	string.SetColor(sf::Color(255, 255, 255, 255));
	string.SetPosition(cars[player].sprite.GetPosition());
	
	if(string.GetPosition().x + string.GetRect().GetWidth() > 800.f) string.Move(sf::Vector2f(800.f - (string.GetPosition().x + string.GetRect().GetWidth()), 0.f));
	if(string.GetPosition().y + string.GetRect().GetHeight() > 600.f) string.Move(sf::Vector2f(0.f, 600.f - (string.GetPosition().y + string.GetRect().GetHeight())));
	
	scoreStrings.push_back(ScoreString(string));
}
